/*MyPhotoIndex Free Photo Organizer <www.myphotoindex.com>
  Copyright (C) <2006-2009>  <Asaf Yarkoni asaf@yarkoni.net>

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.IO;
using MyPhotoIndex.Controls;
using System.Drawing.Drawing2D;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Collections.ObjectModel;
using MyPhotoIndex.Utilities;
using MyPhotoIndex.Interfaces;
using System.Runtime.InteropServices;
using System.Windows.Forms.VisualStyles;
using MyPhotoIndex.Interfaces.Imaging;

namespace MyPhotoIndex.Controls
{
    //  public enum ThumbnailSelectionMethod { Standard = 0, CheckBoxes = 1, InvertedCheckBoxes = 2, Complex = 3 }

    [Description("MyPhotoIndex Thumbnail viewer")]
    [ToolboxBitmap("ThumbnailViewerIcon1")]
    [Designer(typeof(MyPhotoIndexThumbnailViewerDesigner))]
    [Serializable]
    public partial class MyPhotoIndexThumbnailViewer : Control
    {
        private delegate void VoidMethodDelegate();

        #region "Events declaration"

        public event EventHandler<ThumbnailDragDropEventArgs> ThumbnailDragDropEvent;
        public event EventHandler<EventArgs> ThumbnailDragLeaveEvent;
        public event EventHandler<DragEventArgs> ThumbnailDragEnterEvent;
        public event EventHandler<ThumbnailDragDropEventArgs> ThumbnailDragOverEvent;
        public event EventHandler<ThumbnailMouseEventArgs> ThumbnailMouseClickEvent;
        public event EventHandler<ThumbnailMouseEventArgs> ThumbnailMouseDoubleClickEvent;
        public event EventHandler<ThumbnailMouseEventArgs> ThumbnailIconMouseEnterEvent;
        public event EventHandler<ThumbnailMouseEventArgs> ThumbnailIconMouseLeaveEvent;
        public event EventHandler<SelectedImageInfo> SelectionChangedEvent;
        public event EventHandler<EventArgs> CheckChangedEvent;

        #endregion "Events declaration"

        #region "Members declaration"

        static Image ms_noImageImage = Resources.Resources.NoImage;

        private int m_imageTopMargin = 10;
        private int m_imageBottomMargin = 10;

        private int m_imageLeftMargin = 10;
        private int m_imageRightMargin = 10;

        private int m_imageSpacing = 20;
        private float m_maxPictureSize;
        private int m_numOfCols;
        private int m_maxNumOfCols;
        private int m_numOfRows = 1;
        private int m_numOfImages;
        private int m_firstImageDrawn;
        private int m_lastImageDrawn;

        private bool m_overideImageSize = false;
        private float m_imageBoxWidth = 32;
        private float m_imageBoxHeight = 32;
        private float m_picWidth;
        private float m_picHeight;
        private float m_picRatio = 0.73F;
        private int m_numOfImagePages;
        private int m_numOfImageRows;
        private float m_imageBoxLeftAndRightBorders;
        private int m_pageLeftBorder;
        private int m_pageTopBorder;
        private VerticalAlignment m_verticalAlignment = VerticalAlignment.Center;

        [NonSerializedAttribute]
        private Timer m_visualTimer;
        private bool m_useSelection = true;
        private bool m_useCheckBoxes = false;

        private bool m_showInfoIcons;
        private bool m_showDetails;
        private string m_noImagesMessage;
        private Font m_noImagesMessageFont;
        private bool m_extendNumberOfRows;
        private Image m_imageMissingImage = ms_noImageImage;

        private List<Thumbnail> m_drawnImages = new List<Thumbnail>();
        private List<int> m_selectedImages = new List<int>();
        private List<int> m_checkedImages = new List<int>();
        private List<int> m_uncheckedImages = new List<int>();

        private int[] m_imageOffsetToId;
        private Dictionary<int, int> m_imageIdToOffset = new Dictionary<int, int>();

        private bool m_allowDrag;
        private bool m_singleSelection;

        private IImageCollection m_imageList;
        private int m_scrollOffset;

        private EventHandler<ImageDataChangedArgs> m_imageChangedEvtHandler;

        private CategoryRenderer m_categoryRenderer = new CategoryRenderer();
        private ThumbnailTextInfo m_thumbnailTextInfoDisplayType;

        #endregion "Members declaration"

        public MyPhotoIndexThumbnailViewer()
        {
            InitializeComponent();

            m_imageOffsetToId = new int[0];

            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

            this.SizeChanged += new EventHandler(PhotoThumbViewer_SizeChanged);
            this.MouseClick += new MouseEventHandler(PhotoThumbViewer_MouseClick);
            this.MouseDoubleClick += new MouseEventHandler(MyPhotoIndexThumbnailViewer_MouseDoubleClick);
            this.MouseMove += new MouseEventHandler(MyPhotoIndexThumbnailViewer_MouseMove);
            this.MouseMove += new MouseEventHandler(MyPhotoIndexThumbnailViewer_MouseMoveDrag);
            this.MouseUp += new MouseEventHandler(MyPhotoIndexThumbnailViewer_MouseUpDragDrop);
            this.MouseDown += new MouseEventHandler(MyPhotoIndexThumbnailViewer_MouseDownDragDrop);

            vScrollBar1.Parent = this;
            vScrollBar1.Minimum = 0;
            vScrollBar1.SmallChange = 1;
            vScrollBar1.LargeChange = 3;

            m_maxNumOfCols = 10;
            m_numOfCols = 3;

            m_visualTimer = new Timer();
            m_visualTimer.Tick += new EventHandler(m_visualTimer_Tick);
            m_visualTimer.Interval = 250;

            m_noImagesMessage = Resources.Resources.ThumbnailViewer_PhotoIndexIsEmpty;
            m_noImagesMessageFont = new Font("family", 30, FontStyle.Bold);

            m_showDetails = true;
            m_extendNumberOfRows = true;

            this.BackColor = Color.FromArgb(227, 239, 255);

            m_imageChangedEvtHandler = new EventHandler<ImageDataChangedArgs>(DrawnImage_ImageLoadedEvent);
        }

        internal void RestartVisualTimer()
        {
            m_visualTimer.Stop();
            m_visualTimer.Start();
        }

        void m_visualTimer_Tick(object sender, EventArgs e)
        {
            m_visualTimer.Stop();
            foreach (Thumbnail thumbnail in m_drawnImages)
            {
                if (thumbnail != null)
                {
                    thumbnail.ValidateThumbnailSize();
                }
            }
        }

        public SizeF MaxImageRectangleSize
        {
            get
            {
                return new SizeF(m_picWidth, m_picHeight);
            }
        }

        public ThumbnailTextInfo ThumbnailTextInfoDisplayType
        {
            get { return m_thumbnailTextInfoDisplayType; }
            set
            {
                m_thumbnailTextInfoDisplayType = value;
                this.Invalidate();
            }
        }

        public CategoryRenderer CategoryRenderer
        {
            get { return m_categoryRenderer; }
            set { m_categoryRenderer = value; }
        }

        [Category("ThumbnailViewer")]
        public Color BackgroundColor
        {
            get { return base.BackColor; }
            set { base.BackColor = value; }
        }

        [Category("ThumbnailViewer")]
        public int ImageTopMargin
        {
            get { return m_imageTopMargin; }
            set { m_imageTopMargin = value; }
        }

        [Category("ThumbnailViewer")]
        public int ImageBottomMargin
        {
            get { return m_imageBottomMargin; }
            set { m_imageBottomMargin = value; }
        }

        [Category("ThumbnailViewer")]
        public VerticalAlignment VerticalAlignment
        {
            get { return m_verticalAlignment; }
            set
            {
                m_verticalAlignment = value;
                calculate();
            }
        }

        [Description("Use double buffering")]
        [Category("ThumbnailViewer")]
        public bool Buffered
        {
            get
            {
                return this.GetStyle(ControlStyles.OptimizedDoubleBuffer);
            }

            set
            {
                this.SetStyle(ControlStyles.OptimizedDoubleBuffer, value);
            }
        }

        [Description("Use single thumbnail selection")]
        [Category("ThumbnailViewer")]
        public bool SingleSelection
        {
            get
            {
                return m_singleSelection;
            }

            set
            {
                m_singleSelection = value;
            }
        }

        [Description("Allow dragging of thumbnails")]
        [Category("ThumbnailViewer")]
        public bool AllowDrag
        {
            get
            {
                return m_allowDrag;
            }

            set
            {
                m_allowDrag = value;
            }
        }

        [Description("No Images Message text")]
        [Category("ThumbnailViewer")]
        [Localizable(true)]
        public string NoImagesMessage
        {
            get { return m_noImagesMessage; }
            set { m_noImagesMessage = value; }
        }

        [Description("No images message font")]
        [Category("ThumbnailViewer")]
        [Localizable(true)]
        public Font NoImagesMessageFont
        {
            get { return m_noImagesMessageFont; }
            set { m_noImagesMessageFont = value; }
        }

        [Description("Image missing thumbnail")]
        [Category("ThumbnailViewer")]
        public Image ImageMissingImage
        {
            get { return m_imageMissingImage; }
            set { m_imageMissingImage = value; }
        }

        //[Description("Thumbnail selection method")]
        //[Category("ThumbnailViewer")]
        //public ThumbnailSelectionMethod SelectionMethod
        //{
        //    get { return m_selectionMethod; }
        //    set { m_selectionMethod = value; }
        //}

        [Description("Thumbnail show selection")]
        [Category("ThumbnailViewer")]
        public bool UseSelection
        {
            get { return m_useSelection; }
            set { m_useSelection = value; }
        }

        [Description("Thumbnail show checkboxes")]
        [Category("ThumbnailViewer")]
        public bool UseCheckBoxes
        {
            get { return m_useCheckBoxes; }
            set { m_useCheckBoxes = value; }
        }

        [Description("Display image info Icons ShowDetails must be set to true in order to use this property")]
        [Category("ThumbnailViewer")]
        public bool ShowInfoIcons
        {
            get { return m_showInfoIcons; }
            set { m_showInfoIcons = value; }
        }

        [Description("Display image data in image frame")]
        [Category("ThumbnailViewer")]
        public bool ShowDetails
        {
            get { return m_showDetails; }
            set { m_showDetails = value; }
        }

        [Description("Define wheather images can overflow screen boundries")]
        [Category("ThumbnailViewer")]
        public bool ExtendNumberOfRows
        {
            get { return m_extendNumberOfRows; }
            set { m_extendNumberOfRows = value; }
        }

        [Description("Define maximum number of image columns to display")]
        [Category("ThumbnailViewer")]
        public int MaximumImageColumns
        {
            get { return m_maxNumOfCols; }
            set { m_maxNumOfCols = value; }
        }

        [Description("Define the number of image columns to display")]
        [Category("ThumbnailViewer")]
        public int ImageColumns
        {
            get { return m_numOfCols; }
            set
            {
                if (value < 1)
                {
                    value = 1;
                }

                if (value > m_maxNumOfCols)
                {
                    value = m_maxNumOfCols;
                }

                RestartVisualTimer();
                m_numOfCols = value;
                BulidImageListArrays();
                calculate();
                ScrollToSelected();
                this.Invalidate();
            }
        }

        [Description("Explicitly define the size of the image")]
        [Category("ThumbnailViewer sizing")]
        public bool OverideImageSize
        {
            get { return m_overideImageSize; }
            set { m_overideImageSize = value; calculate(); }
        }

        [Description("Width")]
        [Category("ThumbnailViewer sizing")]
        public float ImageBoxWidth
        {
            get { return m_imageBoxWidth; }
            set { m_imageBoxWidth = value; calculate(); }
        }

        [Description("Height")]
        [Category("ThumbnailViewer sizing")]
        public float ImageBoxHeight
        {
            get { return m_imageBoxHeight; }
            set { m_imageBoxHeight = value; calculate(); }
        }

        [Description("Hide or show the scrollbar")]
        [Category("ThumbnailViewer")]
        public bool ScrollbarVisible
        {
            get { return this.vScrollBar1.Visible; }
            set { this.vScrollBar1.Visible = value; }
        }

        public ReadOnlyCollection<int> SelectedImages
        {
            get { return m_selectedImages.AsReadOnly(); }
        }

        public ReadOnlyCollection<int> CheckedImages
        {
            get { return m_checkedImages.AsReadOnly(); }
        }

        public ReadOnlyCollection<int> UncheckedImages
        {
            get { return m_uncheckedImages.AsReadOnly(); }
        }

        public void SelectAll()
        {
            m_selectedImages.Clear();
            m_selectedImages.AddRange(m_imageIdToOffset.Keys);

            Invalidate();

            if (SelectionChangedEvent != null)
            {
                SelectionChangedEvent.Invoke(this, SelectedImageInfo.Empty);
            }
        }

        public void CheckAll()
        {
            m_checkedImages.Clear();
            m_uncheckedImages.Clear();
            foreach (int imageId in m_imageIdToOffset.Keys)
            {
                m_checkedImages.Add(imageId);
            }

            Invalidate();

            if (CheckChangedEvent != null)
            {
                CheckChangedEvent.Invoke(this, EventArgs.Empty);
            }
        }

        public void InvertSelection()
        {
            List<int> selectedList = new List<int>();
            foreach (int selected in m_selectedImages)
            {
                selectedList.Add(selected);
            }

            m_selectedImages.Clear();
            foreach (int imageId in m_imageIdToOffset.Keys)
            {
                if (selectedList.Contains(imageId) == false)
                {
                    m_selectedImages.Add(imageId);
                }
            }

            Invalidate();

            if (SelectionChangedEvent != null)
            {
                SelectionChangedEvent.Invoke(this, SelectedImageInfo.Empty);
            }
        }

        public void ClearSelection()
        {
            ClearSelection(false);
        }

        public void ClearSelection(bool raiseEvent)
        {
            foreach (int selectedImageId in m_selectedImages)
            {
                Thumbnail drawnImage = GetDrawnImage(selectedImageId);
                if (drawnImage != null)
                {
                    using (Graphics graphics = this.CreateGraphics())
                    {
                        this.Invalidate(drawnImage.FrameDrawingRect);
                    }
                }
            }

            m_selectedImages.Clear();

            if (raiseEvent == true && 
                SelectionChangedEvent != null)
            {
                SelectionChangedEvent.Invoke(this, SelectedImageInfo.Empty);
            }
        }

        public void ClearChecked()
        {
            foreach (int selectedImageId in m_checkedImages)
            {
                Thumbnail drawnImage = GetDrawnImage(selectedImageId);
                if (drawnImage != null)
                {
                    using (Graphics graphics = this.CreateGraphics())
                    {
                        this.Invalidate(drawnImage.FrameDrawingRect);
                    }
                }
            }

            m_checkedImages.Clear();
            m_uncheckedImages.Clear();
            foreach (int imageId in m_imageIdToOffset.Keys)
            {
                m_uncheckedImages.Add(imageId);
            }

            if (CheckChangedEvent != null)
            {
                CheckChangedEvent.Invoke(this, EventArgs.Empty);
            }
        }

        public void SelectImage(int[] imageIds)
        {
            ClearSelection();
            m_selectedImages.AddRange(imageIds);

            if (SelectionChangedEvent != null)
            {
                SelectionChangedEvent.Invoke(this, SelectedImageInfo.Empty);
            }
        }

        public void SelectImage(int imageId, bool select)
        {
            SelectImage(imageId, select, SelectedImageInfo.Empty);
        }

        public void SelectImage(int imageId, bool select, SelectedImageInfo lastImageSelected)
        {
            if (select == true)
            {
                if (m_selectedImages.Contains(imageId) == true ||
                  (ImageIdToImageOffset(imageId) == -1))
                {
                    return;
                }

                m_selectedImages.Add(imageId);
            }
            else
            {
                if (m_selectedImages.Contains(imageId) == false)
                {
                    return;
                }

                m_selectedImages.Remove(imageId);
            }

            Thumbnail drawnImage = GetDrawnImage(imageId);
            if (drawnImage != null)
            {
                using (Graphics graphics = this.CreateGraphics())
                {
                    this.Invalidate(drawnImage.FrameDrawingRect);
                }
            }

            if (SelectionChangedEvent != null)
            {
                SelectionChangedEvent.Invoke(this, lastImageSelected);
            }
        }

        public IImageCollection ImageList
        {
            get { return m_imageList; }
            set
            {
                m_imageList = value;
                m_selectedImages.Clear();

                m_checkedImages.Clear();
                m_uncheckedImages.Clear();
                foreach (int imageId in m_imageIdToOffset.Keys)
                {
                    m_uncheckedImages.Add(imageId);
                }

                this.Refresh();
            }
        }

        public int NumberOfImagesDisplayed
        {
            get { return m_numOfImages; }
        }

        internal void DrawnImage_ImageLoadedEvent(object sender, ImageDataChangedArgs e)
        {
            if (IsDisplayed(e.Id))
            {
                ImageInstance image = (ImageInstance)sender;
                this.Invalidate(image.FrameDrawingRect);
                Thumbnail drawnImage = GetDrawnImage(e.Id);
                if (drawnImage != null)
                {
                    Rectangle frameRect;
                    Rectangle imageRect;
                    GetDrawingRects(ImageIdToImageOffset(e.Id), image.ImageSize.Width, image.ImageSize.Height, out imageRect, out frameRect, m_firstImageDrawn);
                    drawnImage.ImageDrawingRect = imageRect;
                    drawnImage.ValidateThumbnailSize();
                }
            }
        }


        public override void Refresh()
        {
            try
            {
                if (this.Handle != null)
                {
                    this.Invoke(new VoidMethodDelegate(RefreshInternal));
                }
            }
            catch (Exception ex)
            {
                Logger.Log(ex);
            }
        }

        internal void RefreshInternal()
        {
            int lastPosition = vScrollBar1.Value;

            BulidImageListArrays();

            foreach (Thumbnail di in m_drawnImages)
            {
                di.Dispose();
            }
            m_drawnImages.Clear();

            calculate();
            if (vScrollBar1.Maximum >= lastPosition)
            {
                vScrollBar1.Value = lastPosition;
            }
            else
            {
                vScrollBar1.Value = 0;
            }

            if (m_mtv != null)
            {
                UpdateMiniThumbSize();
                m_mtv.Refresh();
            }
            //    this.Invalidate();
        }

        private void BulidImageListArrays()
        {
            int numberOfImages = (m_imageList == null) ? 0 : m_imageList.Count;

            m_imageOffsetToId = new int[numberOfImages];
            m_imageIdToOffset = new Dictionary<int, int>(numberOfImages);

            if (m_imageList != null)
            {
                int imageOffset = 0;
                foreach (IImageData imageData in m_imageList)
                {
                    m_imageOffsetToId[imageOffset] = imageData.Id;
                    m_imageIdToOffset.Add(imageData.Id, imageOffset++);
                }
            }
        }

        public void ToggleImageInfo(int imageId, bool animate)
        {
            Thumbnail drawnImage = GetDrawnImage(imageId);
            if (drawnImage != null)
            {
                using (Graphics graphics = this.CreateGraphics())
                {
                    drawnImage.ShowImageInfo = !drawnImage.ShowImageInfo;
                    DrawOneImage(graphics, imageId);
                }
            }
        }

        internal int ImageOffsetToImageId(int offset)
        {
            if (m_imageOffsetToId.Length == 0 ||
                m_imageOffsetToId.Length < (offset + 1))
            {
                return -1;
            }

            return m_imageOffsetToId[offset];
        }

        internal int ImageIdToImageOffset(int imageId)
        {
            try
            {
                if (m_imageIdToOffset.Count > 0)
                {
                    return m_imageIdToOffset[imageId];
                }
            }
            catch
            {
                return -1;
            }

            return -1;
        }

        public void UpdateScrollBars()
        {
            vScrollBar1.Left = this.Right - vScrollBar1.Width - this.Left;
            vScrollBar1.Height = this.Height;

            calculate();
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            if (this.ClientRectangle.Contains(e.Location) == false)
            {
                base.OnMouseWheel(e);
                return;
            }

            if (e.Delta < 0)
            {
                if (vScrollBar1.Value < vScrollBar1.Maximum)
                {
                    vScrollBar1.Value += vScrollBar1.SmallChange;
                }
            }
            else
            {
                if (vScrollBar1.Value > vScrollBar1.Minimum)
                {
                    vScrollBar1.Value -= vScrollBar1.SmallChange;
                }
            }

            base.OnMouseWheel(e);
        }

        private void PhotoThumbViewer_SizeChanged(Object sender, EventArgs e)
        {
            vScrollBar1.Left = this.Right - vScrollBar1.Width - this.Left;
            vScrollBar1.Height = this.Height;

            RestartVisualTimer();
            calculate();
            UpdateMiniThumbSize();
            this.Invalidate();
        }

        private void vScrollBar1_ValueChanged(object sender, EventArgs e)
        {
            int offset = m_scrollOffset - vScrollBar1.Value;
            m_scrollOffset = vScrollBar1.Value;

            RestartVisualTimer();
            calculate(false);

            this.Invalidate();
        }


        void calculate()
        {
            calculate(true);
        }

        void calculate(bool full)
        {
            int numOfImagesInBank = 0;
            if (m_imageList != null)
            {
                numOfImagesInBank = m_imageList.Count;
            }

            if (full == true)
            {
                if (m_imageList == null)
                {
                    vScrollBar1.Enabled = false;
                    return;
                }

                m_maxPictureSize = this.Width - vScrollBar1.Width - 2;

                if (m_overideImageSize == false)
                {
                    m_imageBoxWidth = (m_maxPictureSize / m_numOfCols);
                    float calcHeight = m_imageBoxWidth * m_picRatio;
                    m_imageBoxHeight = Math.Min(calcHeight, (this.ClientSize.Height > 0) ? this.ClientSize.Height : calcHeight);
                }

                if (m_showDetails == false)
                {
                    m_imageSpacing = 0;
                }

                m_imageBoxLeftAndRightBorders = ((m_imageLeftMargin + m_imageRightMargin) * m_picRatio);

                m_picWidth = m_imageBoxWidth - m_imageBoxLeftAndRightBorders;

                m_picHeight = m_imageBoxHeight - (m_imageTopMargin + m_imageBottomMargin + m_imageSpacing);
             //   m_imageBoxHeight += m_imageSpacing;

                m_numOfRows = Convert.ToInt32(Math.Floor((this.Height) / (m_imageBoxHeight)));
                if (m_numOfRows < 1)
                {
                    m_numOfRows = 1;
                }

                m_numOfImages = (m_numOfCols * m_numOfRows);
            }

            m_firstImageDrawn = (vScrollBar1.Value * m_numOfCols);
            m_lastImageDrawn = m_firstImageDrawn + m_numOfImages;
            if (m_extendNumberOfRows == true)
            {
                if (m_lastImageDrawn < numOfImagesInBank)
                {
                    int leftImages = numOfImagesInBank - m_lastImageDrawn;
                    m_lastImageDrawn += Math.Min(leftImages, m_numOfCols);
                }
            }

            m_numOfImageRows = (int)Math.Ceiling(((float)numOfImagesInBank / (float)m_numOfCols));
            m_numOfImagePages = Convert.ToInt32(Math.Ceiling((float)(m_numOfImageRows / (float)m_numOfRows)));

            int totalDrawnWidth = (int)(m_numOfCols * m_imageBoxWidth);
            m_pageLeftBorder = (int)((m_maxPictureSize - totalDrawnWidth) / 2);

            if (m_extendNumberOfRows == false)
            {
                int numOfDrawnImages = Math.Min((m_lastImageDrawn - m_firstImageDrawn), numOfImagesInBank);

                float drawnRows = (float)numOfDrawnImages / (float)m_numOfCols;
                drawnRows = Math.Min(drawnRows, m_numOfImageRows);
                int totalDrawnHeight = (int)(Convert.ToInt32(Math.Ceiling((float)drawnRows)) * m_imageBoxHeight);

                if (m_verticalAlignment == VerticalAlignment.Center)
                {
                    m_pageTopBorder = (int)((this.ClientRectangle.Height - totalDrawnHeight) / 2);
                }
                else
                    if (m_verticalAlignment == VerticalAlignment.Bottom)
                    {
                        m_pageTopBorder = (this.ClientRectangle.Height - totalDrawnHeight);
                    }
                    else
                    {
                        m_pageTopBorder = 0;
                    }
            }
            else
            {
                m_pageTopBorder = 0;
            }

            if (full == true)
            {
                vScrollBar1.Maximum = Math.Max((m_numOfImageRows - m_numOfRows + 1), 0);
                vScrollBar1.Enabled = (m_numOfImagePages > 1) ? true : false;
            }
        }

        protected override void OnPaintBackground(PaintEventArgs pevent)
        {
            Graphics graphics = pevent.Graphics;

            Color firstColor = this.BackColor;// Color.FromArgb(227, 239, 255);

            //if (e.ClipRectangle.Y == 1)
            //{
            //    Rectangle newClipRect = e.ClipRectangle;
            //    newClipRect.Y = 0;
            //    e.Graphics.Clip = new Region(newClipRect);
            //}

            graphics.Clear(firstColor);

            if (m_imageList == null ||
                m_imageList.Count == 0)
            {
                string str = m_noImagesMessage;
                SizeF stringSize = graphics.MeasureString(str, m_noImagesMessageFont);
                Point start = new Point((int)((this.Width / 2) - (stringSize.Width / 2)), (int)((this.Height / 2) - (stringSize.Height / 2)));

                graphics.DrawString(str, m_noImagesMessageFont, Brushes.Gray, start);
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            if (m_imageList == null ||
                e.ClipRectangle == Rectangle.Empty)
            {
                return;
            }

            System.Drawing.Graphics graphics = e.Graphics;
            graphics.CompositingQuality = CompositingQuality.HighQuality;
            graphics.SmoothingMode = SmoothingMode.HighQuality;
            graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;

            Rectangle drawingRect = this.ClientRectangle;
            drawingRect.Width -= this.vScrollBar1.Width;
            drawingRect.Y = 1;
            drawingRect.Height = this.ClientRectangle.Height - 1;
            if (e.ClipRectangle != drawingRect)
            {
                foreach (Thumbnail image in m_drawnImages)
                {
                    if (image.FrameDrawingRect == e.ClipRectangle)
                    {
                        DrawOneImage(graphics, image.ImageId);
                        return;
                    }
                }
            }

            List<Thumbnail> drawnImages = new List<Thumbnail>();
            int numOfImagesInBank = m_imageList.Count;

            for (int imageOffset = m_firstImageDrawn; imageOffset < m_lastImageDrawn && imageOffset < numOfImagesInBank; imageOffset++)
            {
                int imageId = ImageOffsetToImageId(imageOffset);
                if (imageId > -1)
                {
                    Thumbnail drawnImage = DrawOneImage(graphics, imageId);
                    drawnImages.Add(drawnImage);
                }


            }

            foreach (Thumbnail di in m_drawnImages)
            {
                if (drawnImages.Contains(di) == false)
                {
                    di.Dispose();
                }
            }

            m_drawnImages.Clear();
            m_drawnImages.AddRange(drawnImages);
        }

        private Thumbnail DrawOneImage(Graphics graphics, int imageId)
        {
            return DrawOneImage(graphics, imageId, m_firstImageDrawn);
        }

        private Thumbnail DrawOneImage(Graphics graphics, int imageId, int firstImageDrawn)
        {
            Thumbnail drawnImage = null;
            foreach (Thumbnail image in m_drawnImages)
            {
                if (image.ImageId == imageId)
                {
                    drawnImage = image;
                    break;
                }
            }

            IImageData imageData = null;
            if (drawnImage == null)
            {
                imageData = m_imageList[imageId];
            }
            else
            {
                imageData = drawnImage.ImageData;
            }

            if (imageData == null)
            {
                return null;
            }

            Rectangle frameRect;
            Rectangle imageRect;
            GetDrawingRects(ImageIdToImageOffset(imageId), imageData.ImageSize.Width, imageData.ImageSize.Height, out imageRect, out frameRect, firstImageDrawn);

            if (drawnImage == null)
            {
                drawnImage = new Thumbnail(imageData, imageRect, frameRect, this);
                drawnImage.ImageDataChangedEvent += m_imageChangedEvtHandler;
                drawnImage.CategoryRenderer = m_categoryRenderer;
            }
            else
            {
                drawnImage.ImageDrawingRect = imageRect;
                drawnImage.FrameDrawingRect = frameRect;
            }

            drawnImage.DrawImage(graphics, IsSelected(imageId), m_useCheckBoxes, IsChecked(imageId), m_showInfoIcons, m_showDetails);

            if (m_showDetails == true)
            {
                ImageFramePainter.DrawBoxFrame(drawnImage, frameRect, graphics);
            }

            return drawnImage;
        }

        internal Rectangle GetImageDrawingRect(int imageId)
        {
            Rectangle frameRect;
            Rectangle imageRect;
            GetDrawingRects(ImageIdToImageOffset(imageId), (int)m_imageBoxWidth, (int)m_imageBoxHeight, out imageRect, out frameRect, m_firstImageDrawn);

            return imageRect;
        }

        internal void GetDrawingRects(int imageOffset, int imageWidth, int imageHeight, out Rectangle imageRect, out Rectangle frameRect, int firstImageDrawn)
        {
            if (imageWidth <= 0)
            {
                imageWidth = 10;
            }

            if (imageHeight <= 0)
            {
                imageHeight = 10;
            }

            int calcImageId = ((imageOffset + 1) - (firstImageDrawn));
            int Y = (int)Math.Ceiling((float)((float)calcImageId / (float)m_numOfCols));
            int X = (m_numOfCols - ((m_numOfCols * Y) - calcImageId));

            X--;
            Y--;

            SizeF realPicSize = ImageAlgo.GetImageProportionalSizeF(new Size(imageWidth, imageHeight), new SizeF(m_picWidth, m_picHeight));
            int realPicWidth = (int)Math.Ceiling(realPicSize.Width);
            int realpicHeight = (int)Math.Ceiling(realPicSize.Height);

            frameRect = new Rectangle((int)(m_imageBoxWidth * X), (int)(m_imageBoxHeight * Y), (int)m_imageBoxWidth, (int)m_imageBoxHeight);
            imageRect = new Rectangle((int)((m_imageBoxWidth * X) + ((m_imageBoxWidth - realPicWidth) / 2)), (int)(m_imageBoxHeight * Y) + m_imageTopMargin, realPicWidth, realpicHeight);

            frameRect.X += m_pageLeftBorder;
            imageRect.X += m_pageLeftBorder;

            frameRect.Y += m_pageTopBorder;
            imageRect.Y += m_pageTopBorder;

            //frameRect.Offset(0, offset.X);
            // imageRect.Offset(0, offset.X);
        }

        public bool IsSelected(int imageId)
        {
            return m_selectedImages.Contains(imageId);
        }

        public bool IsChecked(int imageId)
        {
            return m_checkedImages.Contains(imageId);
        }

        public bool IsDisplayed(int imageId)
        {
            int imageOffset = ImageIdToImageOffset(imageId);
            if (imageOffset >= m_firstImageDrawn && imageOffset < m_lastImageDrawn)
            {
                return true;
            }

            return false;
        }

        public Thumbnail GetDrawnImage(int imageId)
        {
            try
            {
                foreach (Thumbnail image in m_drawnImages)
                {
                    if (image.ImageId == imageId)
                    {
                        return image;
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Log(ex);
            }

            return null;
        }

        public void ScrollToSelected()
        {
            int selectedImageId = -1;

            foreach (int imageId in m_selectedImages)
            {
                if (m_imageIdToOffset.ContainsKey(imageId) == true)
                {
                    selectedImageId = imageId;
                    break;
                }
            }

            if (0 <= selectedImageId &&
                IsDisplayed(selectedImageId) == false)
            {
                int scrollOffset = (ImageIdToImageOffset(selectedImageId) / m_numOfCols);
                vScrollBar1.Value = Math.Min(vScrollBar1.Maximum, scrollOffset);
            }
        }

        public void SelectFirstImage()
        {
            try
            {
                ClearSelection();

                int firstImage = ImageOffsetToImageId(0);
                SelectImage(firstImage, true);
                ScrollToSelected();
            }
            catch { }
        }

        private void MyPhotoIndexThumbnailViewer_MouseEnter(object sender, EventArgs e)
        {
            //    this.Select();
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            this.Select();
        }

        public Thumbnail GetThumbnailFromPosition(Point position)
        {
            Thumbnail newSelectedImage = null;
            foreach (Thumbnail image in m_drawnImages)
            {
                if (image.FrameDrawingRect.Contains(base.PointToClient(position)))
                {
                    newSelectedImage = image;
                    break;
                }
            }

            return newSelectedImage;
        }

        private void MyPhotoIndexThumbnailViewer_DragDrop(object sender, DragEventArgs e)
        {
            Thumbnail newSelectedImage = GetThumbnailFromPosition(new Point(e.X, e.Y));
            if (newSelectedImage == null)
            {
                return;
            }

            if (ThumbnailDragDropEvent != null)
            {
                ThumbnailDragDropEvent.Invoke(this, new ThumbnailDragDropEventArgs(e, newSelectedImage.ImageId));
            }
        }

        private void MyPhotoIndexThumbnailViewer_DragOver(object sender, DragEventArgs e)
        {
            Thumbnail newSelectedImage = GetThumbnailFromPosition(new Point(e.X, e.Y));
            int imageId = -1;
            if (newSelectedImage != null)
            {
                imageId = newSelectedImage.ImageId;
            }

            if (ThumbnailDragOverEvent != null)
            {
                ThumbnailDragDropEventArgs eventArgs = new ThumbnailDragDropEventArgs(e, imageId);
                ThumbnailDragOverEvent.Invoke(this, eventArgs);
                e.Effect = eventArgs.Effect;
            }
        }

        private void ThumbnailViewer_DragLeave(object sender, EventArgs e)
        {
            if (ThumbnailDragLeaveEvent != null)
            {
                ThumbnailDragLeaveEvent.Invoke(this, e);
            }
        }

        private void ThumbnailViewer_DragEnter(object sender, DragEventArgs e)
        {
            if (ThumbnailDragEnterEvent != null)
            {
                ThumbnailDragEnterEvent.Invoke(this, e);
            }
        }

        public override bool Focused
        {
            get
            {
                if (base.Focused ||
                    vScrollBar1.Focused)
                {
                    return true;
                }

                return false;
            }
        }

        protected override void OnGotFocus(EventArgs e)
        {
            Invalidate();
            base.OnGotFocus(e);
        }

        protected override void OnLostFocus(EventArgs e)
        {
            Invalidate();
            base.OnLostFocus(e);
        }

        public void FlashThumbnailTagIcons(int[] images)
        {
            System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(FlashThumbnailTagIconsAsync), images);
        }

        public delegate void FlashTagDelegate(bool on);
        public void FlashThumbnailTagIconsAsync(object state)
        {
            int[] images = state as int[];
            List<Thumbnail> thumbnails = new List<Thumbnail>();
            foreach (int imageId in images)
            {
                if (this.IsDisplayed(imageId) == true)
                {
                    Thumbnail thumbnail = GetDrawnImage(imageId);
                    if (thumbnail != null)
                    {
                        thumbnails.Add(thumbnail);
                    }
                }
            }

            for (int i = 0; i < 5; i++)
            {
                foreach (Thumbnail thumb in thumbnails)
                {
                    this.Invoke(new FlashTagDelegate(thumb.FlashTagIcon), new object[] { true });
                }

                System.Threading.Thread.Sleep(200);

                foreach (Thumbnail thumb in thumbnails)
                {
                    this.Invoke(new FlashTagDelegate(thumb.FlashTagIcon), new object[] { false });
                }
            }

        }
    }
}
